//
//  HPRequest.m
//  Network
//
//  Created by phq on 2019/7/4.
//  Copyright © 2019 phq. All rights reserved.
//

#import "HPRequest.h"
#import "HPNetworkEngine.h"
#import "HPHelper.h"
#import "HPDefine.h"

static int requestFailCount = 5;

NSString * const PHNetworkDidChangeNotification = @"PHNetworkingReachabilityDidChange";
static NSString * const k_mac_address_key = @"hprequestkit_mac_address_key";

@interface HPRequest ()
/* private property */
@property (nonatomic,strong) NSString *deviceId;
@property (nonatomic,strong) NSString *appTag;
//@property (nonatomic,strong) NSString *apiFlag;
/* log controll */
@property (nonatomic,assign) BOOL isRequestParamsLog;
@property (nonatomic,assign) BOOL isRequestHeaderLog;
/* reqest controll */
@property (nonatomic,strong) NSDictionary <NSString *,HPInfo *>*requestObjs;
@property (nonatomic,strong) HPInfo *requestJavas;
@end

@implementation HPRequest

+ (HPRequest *)shareInstance {
    @synchronized (self) {
        static HPRequest *_manager = nil;
        static dispatch_once_t oncePredicate;
        dispatch_once(&oncePredicate, ^{
            printf("-----------------\n");
            printf("↓ ↓ ↓ 看这里 ↓ ↓ ↓\n");
            printf("|--- 🚀🚀 HPNetworking version ------ > v4.1 🚀🚀---|\n");
            printf("↑ ↑ ↑ 看这里 ↑ ↑ ↑\n");
            printf("-----------------\n");
            
            _manager = [[HPRequest alloc] init];
            _manager.isRequestParamsLog = NO;
            _manager.requestObjs = [NSMutableDictionary dictionary];
            _manager.access = HPNetworkAccessRelease;
            // response data config
            NetEngineInstance.netConfig.responseDataType = ResponseDataTypeSerializer;
            NetEngineInstance.netConfig.requestMethod = RequestMethodPost;
            
            // set https header
            [NetEngineInstance setHttpHeaderValue:@"application/x-www-form-urlencoded;charset=utf-8" httpHeaderField:@"Content-Type"];
            [NetEngineInstance setHttpHeaderValue:@"zh-cn" httpHeaderField:@"accept-language"];
            [NetEngineInstance setHttpHeaderValue:_manager.deviceId httpHeaderField:@"MAC"];
            NSString *ver = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
            NSString *value = [NSString stringWithFormat:@"APPS_IOS_IPHONE,%@,%@",[HPRequest getApiFlag],ver];
            [NetEngineInstance setHttpHeaderValue:value httpHeaderField:@"SYS"];
            [NetEngineInstance setHttpHeaderValue:@"94cbe78f3bcec8be81d68e7bdfb9ad9b" httpHeaderField:@"SecretKey"]; // 职业大全请求头必带，否则无返回（龚祥）
            [NetEngineInstance setHttpHeaderValue:@"1" httpHeaderField:@"platform"];// //职业大全请求头必带。请求来源：1 小程序请求  2 pc请求（龚祥）

        });
        return _manager;
    }
}

+ (NSString *_Nonnull)getMacAddress {
    return HPNetworking.deviceId;
}

#pragma mark - post request
- (void)postWithModel:(__kindof HPRequestModel *_Nonnull)model succeed:(void (^_Nullable)(id _Nullable result))bSucceed failure:(void (^_Nullable)( NSString*_Nullable error))bFailure {
    
    HPInfo *info = [self infoOnType:model.type];
    
    NSDictionary *dict = [info encodePostParams:model.postParams getParams:model.getParams];
    NSString *url = [dict objectForKey:@"path"];
    NSDictionary *paramDic = [dict objectForKey:@"param"];
    [self configHeader:model];
    [self logParams:model url:url params:paramDic];
    
    __weak typeof(self) weakSelf = self;
    [NetEngineInstance httpsRequestPath:url parameters:paramDic success:^(NSURLSessionDataTask *task, id  _Nullable responseObject) {
        [weakSelf logTask:task];
        id result = [info decodeObject:responseObject model:model];
        if (result == nil) {
            NSString *logMsg = [NSString stringWithFormat:@"\n-----url: %@ \n-----get: %@ \n-----post: %@\n-----result: %@\n",url,model.getParams, model.postParams, result];
            HPLog(@"\n🦋🦋🦋🔥[火] Request succeeded:%@",logMsg);
            if (bFailure) bFailure(@"数据解释失败");
            return;
        }
        
       
        if ([result isKindOfClass:[NSDictionary class]] && ((NSDictionary *)result).count > 0) {
            NSString *logMsg = [NSString stringWithFormat:@"\n-----url: %@ \n-----get: %@ \n-----post: %@\n-----result: %@\n",url,model.getParams, model.postParams, [HPHelper stringWithJson:result]];
            HPLog(@"\n🦋🦋🦋🦋 Request succeeded:%@",logMsg);
            
            NSString *code = (NSString *)[result objectForKey:@"code"];
            if (code.intValue == 403 || code.intValue == -5) {
                HPLog(@"\n🦋🦋🔥🔥[炎] Request lose efficacy: token(%zd)",model.type);
                [weakSelf configAccessToken:model.type forcedUpdate:YES];
                if (bFailure) bFailure(@"token失效,请稍候再试");
                return;
            }
        }else {
            NSString *logMsg = [NSString stringWithFormat:@"\n-----url: %@ \n-----get: %@ \n-----post: %@\n-----result: %@\n",url,model.getParams, model.postParams, result];
            HPLog(@"\n🦋🦋🦋🦋 Request succeeded:%@",logMsg);
        }
        if (bSucceed) bSucceed(result);
    } failure:^(NSURLSessionDataTask *task,NSError * _Nullable error) {
        [weakSelf logTask:task];
        HPLog(@"\n🔥🔥🔥🔥[燚] Request failure:\n-----url: %@\n-----get: %@ \n-----post: %@\n-----error: %@\n", info.url,model.getParams,model.postParams,error.description);
//        NSData * data = error.userInfo[@"com.alamofire.serialization.response.error.data"];
//        NSString * str = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//        NSLog(@"--:%@",str);
        
        if (bFailure) bFailure(error.description);
        if (error.code == 3840) {
            HPLog(@"token失效，请重新请求token------------->%zd",model.type);
            [weakSelf configAccessToken:model.type forcedUpdate:YES];
        }
    }];
}

#pragma mark - network status

- (BOOL)hasNetwork {
    return [NetEngineInstance isReachable];
}

- (HPNetworkStatus)networkStatus {
    return ((HPNetworkStatus)[NetEngineInstance networkStatus]);
}

- (void)startMonitoring {
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        [[NSNotificationCenter defaultCenter] postNotificationName:PHNetworkDidChangeNotification object:nil userInfo:@{@"status": @(status).stringValue}];
    }];
}

#pragma mark - init api

- (void)initPhpApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    ATokenType type;
    if ([[HPRequest getApiFlag] isEqualToString:@"YW"]) type = ATokenTypeYw;
    else type = ATokenTypeIphone;
    [self initWebServe:type ver:ver url:url end:complete];
}
- (void)initPhpApi4:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    if (url==nil || ver==nil) complete(NO,-1);
    for (ATokenType i=ATokenTypeJjr; i<=ATokenTypeYlzhadui; i++) {
        [self initWebServe:i ver:ver url:url end:complete];
    }
}
- (void)initYewenApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeTraining ver:ver url:url end:complete];
}
- (void)initJavaApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeJava ver:ver url:url end:complete];
}
- (void)initJavaLiveApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeJavaLive ver:ver url:url end:complete];
}
- (void)initJavaYpApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeJavaYp ver:ver url:url end:complete];
}
- (void)initJavaNewApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeJavaNew ver:ver url:url end:complete];
}
- (void)initJavaWebServeApi:(NSString *_Nonnull)url ver:(NSString *_Nonnull)ver end:(HPCallBlock _Nullable)complete {
    [self initWebServe:ATokenTypeJavaWebServe ver:ver url:url end:complete];
}


- (void)initJavaWebServe:(HPCallBlock _Nullable)complete {
    HPInfo *info = [[HPInfo alloc] init];
    info.urlPrefix = [self currentAccess];
    info.urlPath = @"/api-recommend";
    info.urlSuffix = @"/open/index";
    info.ver = @"v1";
    info.url = [NSString stringWithFormat:@"%@%@%@",info.urlPrefix,info.urlPath,info.urlSuffix];
    info.complete = complete;
    info.type = ATokenTypeJavas;
    [_requestObjs setValue:info forKey:[self infoKeyOnType:info.type]];
    [info infoForcedUpdate:NO];
}

- (void)initWebServe:(ATokenType)type ver:(NSString *_Nonnull)ver url:(NSString *_Nonnull)url end:(HPCallBlock _Nullable)complete {
    if (url==nil || ver==nil) complete(NO,type);
    HPInfo *info = [[HPInfo alloc] init];
    info.url = url;
    info.ver = ver;
    info.complete = complete;
    info.type = type;
    [_requestObjs setValue:info forKey:[self infoKeyOnType:type]];
    [info infoForcedUpdate:NO];
}

- (void)configAccessToken:(ATokenType)type forcedUpdate:(BOOL)update {
    // 强制更新次数最多5次，防止请求403无限请求
    if (update) {
        if (requestFailCount <= 0) {
            return;
        }
        requestFailCount --;
    }
    HPInfo *info = [self infoOnType:type];
    [info infoForcedUpdate:update];
}

- (HPInfo *)infoOnType:(ATokenType)type {
    HPInfo *info = [_requestObjs objectForKey:[self infoKeyOnType:type]];
    return info;
}

- (NSString *)currentAccess {
    NSString *url;
    switch (_access) {
        case HPNetworkAccessDebug:
            url = @"http://producttest.yl1001.com";
            break;
        case HPNetworkAccessPre:
            url = @"http://productpre.yl1001.com";
            break;
        case HPNetworkAccessDRY:
            url = self.accessDRY;
            break;
        default:
            url = @"http://product.yl1001.com";
            break;
    }
    return url;
}

// 使每个type对应一个info对象，区分域名和用户名。各服务体系互不干扰。
- (NSString *)infoKeyOnType:(ATokenType)type {
    return [NSString stringWithFormat:@"%@_%d",HPUserType(type),(int)type];
}

#pragma mark - Private API

- (void)configHeader:(__kindof HPRequestModel *_Nonnull)model {
    HPInfo *info = [self infoOnType:model.type];
    NSString *value = [NSString stringWithFormat:@"%@,%@,%@,%@",_userId,self.deviceId,self.appTag,info.ver];
    [NetEngineInstance setHttpHeaderValue:value httpHeaderField:@"User-Agent"];
    [NetEngineInstance setHttpHeaderValue:value httpHeaderField:@"AUTO-MARK"];
    [NetEngineInstance setHttpHeaderValue:_userId httpHeaderField:@"uid"];
    [NetEngineInstance setHttpHeaderValue:model.dateGMT httpHeaderField:@"date"];
}

// ** This attribute needs to be consistent with the old and new versions. Otherwise, the token generated by the old version using the old mac address is inconsistent with the newly generated mac address of the request header parameter in the new version, causing the request to fail. The interface request returns an error message: string format "ZTj27Nlds0F7Aqp3yh8l9su56rTyzZyM ”
- (NSString *)deviceId {
    if (_deviceId == nil) {
        NSString *macAddress = [[NSUserDefaults standardUserDefaults] objectForKey:k_mac_address_key];
        if (macAddress == nil) {
            macAddress = [UIDevice currentDevice].identifierForVendor.UUIDString;
            [[NSUserDefaults standardUserDefaults] setValue:macAddress forKey:k_mac_address_key];
        }
        _deviceId = macAddress?macAddress:@"AAA";
    }
    return _deviceId;
}

+ (NSString *)getApiFlag {
    NSString *apiFlag;
    NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
    if ([bundleId isEqualToString:@"com.yl1001.yewen"] || [bundleId isEqualToString:@"com.yl1001.yewenbeta"]) {
        apiFlag = @"YW";
    }else if ([bundleId isEqualToString:@"com.job1001.jobClient"] || [bundleId isEqualToString:@"com.job1001.jobClientBeta"]) {
        apiFlag = @"YL1001_GROUP";
    }
    return apiFlag;
}

- (NSString *)appTag {
    if (_appTag == nil) {
        NSString *bundleId = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];
        NSString *flag;
        if ([bundleId isEqualToString:@"com.job1001.jobClient"] ||
            [bundleId isEqualToString:@"com.job1001.jobClientBeta"]) {
            flag = @"iphone_yl1001new_client";
        }else {
            flag = @"iphone_yw_client";
        }
        _appTag = flag;
    }
    return _appTag;
}


- (void)logHeader:(BOOL)logHeader params:(BOOL)logParams {
    _isRequestHeaderLog = logHeader;
    _isRequestParamsLog = logParams;
}

// request header info
- (void)logTask:(NSURLSessionDataTask *)task {
    if (_isRequestHeaderLog) {
        NSString *url = task.originalRequest.URL.absoluteString;
        NSString *method = task.originalRequest.HTTPMethod;
        NSDictionary *header = task.originalRequest.allHTTPHeaderFields;
        NSString *body = [[NSString alloc] initWithData:task.originalRequest.HTTPBody encoding:NSUTF8StringEncoding];
        NSHTTPURLResponse *response = (NSHTTPURLResponse *)task.response;
        
        printf("↓ ↓ ↓ ↓ ↓ ↓\n");
        printf("---------🚀请求头信息🚀--------\n");
        NSLog(@"\n---请求URL:%@\n---请求方式:%@\n---请求头:%@\n---响应头:%@\n",url,method,header,response.allHeaderFields);
        NSLog(@"\n---请求体:%@\n",body);
        printf("↑ ↑ ↑ ↑ ↑ ↑\n");
        printf("-----------------\n");
    }
}

// log for debug
- (void)logParams:(__kindof HPRequestModel *_Nonnull)model url:(NSString *)url params:(NSDictionary *)params{
    if (_isRequestParamsLog) {
        HPInfo *request = [self infoOnType:model.type];
        printf("↓ ↓ ↓ ↓ ↓ ↓\n");
        printf("---------🚀参数加密【前】🚀--------\n");
        NSLog(@"---------http url = %@",request.url);
        NSLog(@"---------http params get = %@",model.getParams);
        NSLog(@"---------http params post = %@",model.postParams);
        printf("---------🚀参数加密【后】🚀--------\n");
        NSLog(@"---------http URL = %@",url);
        NSLog(@"---------http post params = %@",params);
        printf("↑ ↑ ↑ ↑ ↑ ↑\n");
        printf("-----------------\n");
    }
}

@end
